/*
 * $Id $
 * Copyright: 2008 Valentin Kolesnikov (javadev75@gmail.com)
 */
package net.sourceforge.jselunit;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlElement;

/**
 * Base test case with parser support.
 *
 * @author Valentin Kolesnikov
 * @version $Revision$ $Date$
 */
public class AbstractParserHtmlTest extends AbstractHtmlUnitTest {

    /**
     * Html file content.
     */
    protected String testhtmldata;

    public void initTestData(String name) {
        try {
            URL url = getClass().getResource("testdata/" + name + ".html");
            assertNotNull("File not found: testdata/" + name + ".html", url);
            testhtmldata = FileUtils.readFileToString(
                    new File(url.toString().replaceAll("%20", " ").replaceFirst("file:", "")));
        } catch (IOException e) {
            fail("can't init " + e.getMessage());
        }
    }

    @SuppressWarnings("unchecked")
    private List<String> loadHtmlStrings() throws Exception {
        loadPageFromString(testhtmldata);
        final List<HtmlElement> elements = (List<HtmlElement>) getPage().getByXPath("//body/table/tbody/tr");
        List<String> result = new ArrayList<String>();
        for (HtmlElement element : elements) {
            result.add(element.asText());
        }
        return result;
    }

   /**
    * Command data.
    */
   protected static class CommandData {
        private String command;
        private String data1;

        public CommandData(String command, String data1) {
            this.command = command;
            this.data1 = data1;
        }

        public String getCommand() {
            return command;
        }

        public String getData1() {
            return data1;
        }
    }

    protected CommandData makeMaps(String string) {
        Pattern pattern = Pattern.compile("(\\w+)\\s+(.*)");
        Matcher matcher = pattern.matcher(string);
        if (matcher.find()) {
            return new CommandData(matcher.group(1), matcher.group(2).trim().replaceAll("\\\\n", "\n"));
        }
        fail("Can't parse string " + string);
        return null;
    }

    private List<CommandData> makeMapsList(List<String> strings) {
        List<CommandData> result = new ArrayList<CommandData>();
        for (String string : strings) {
            result.add(makeMaps(string));
        }
        return result;
    }

    protected List<CommandData> getSrcData() {
        List<String> strings = new ArrayList<String>();
        try {
            strings = loadHtmlStrings();
        } catch (Exception e) {
            fail("Can't get src data " + e.getMessage());
        }
        return makeMapsList(strings);
    }

    interface Command {
        boolean isPresent(String data);
        void execute(String ... params) throws Exception;
    }

    class OpenCommand implements Command {
        public boolean isPresent(String data) {
            return "open".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            if (params[0].startsWith("/")) {
                open(params[1] + "/" + params[0]);
            } else {
                open(params[0]);
            }
        }
    }

    class AssertElementPresentCommand implements Command {
        public boolean isPresent(String data) {
            return "assertElementPresent".equals(data);
        }
        public void execute(String ... params) {
            assertElementPresent(params[0]);
        }
    }

    class AssertElementNotPresentCommand implements Command {
        public boolean isPresent(String data) {
            return "assertElementNotPresent".equals(data);
        }
        public void execute(String ... params) {
            assertElementNotPresent(params[0]);
        }
    }

    class AssertAttributeCommand implements Command {
        public boolean isPresent(String data) {
            return "assertAttribute".equals(data);
        }
        public void execute(String ... params) {
            assertAttribute(params[0].replaceFirst("(\\S+).*", "$1"), params[0].replaceFirst("\\S+\\s+", ""));
        }
    }

    class ClickCommand implements Command {
        public boolean isPresent(String data) {
            return "click".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            click(params[0]);
        }
    }

    class ClickAndWaitCommand implements Command {
        public boolean isPresent(String data) {
            return "clickAndWait".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            clickAndWait(params[0]);
        }
    }

    class TypeCommand implements Command {
        public boolean isPresent(String data) {
            return "type".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            type(params[0].replaceFirst("(\\S+).*", "$1"), params[0].replaceFirst("\\S+\\s+", ""));
        }
    }

    class WaitForElementPresentCommand implements Command {
        public boolean isPresent(String data) {
            return "waitForElementPresent".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
            waitForElementPresent(params[0]);
        }
    }

    class WaitForAttribute implements Command {
        public boolean isPresent(String data) {
            return "waitForAttribute".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
            waitForAttribute(params[0].replaceFirst("(\\S+).*", "$1"), params[0].replaceFirst("\\S+\\s+", ""));
        }
    }

    class AssertTitleCommand implements Command {
        public boolean isPresent(String data) {
            return "assertTitle".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
            assertTitle(params[0]);
        }
    }

    class EchoCommand implements Command {
        public boolean isPresent(String data) {
            return "echo".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException {
            echo(params[0]);
        }
    }

    class GoBackAndWaitCommand implements Command {
        public boolean isPresent(String data) {
            return "goBackAndWait".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            goBackAndWait();
        }
    }

    class SelectFrameCommand implements Command {
        public boolean isPresent(String data) {
            return "selectFrame".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            selectFrame(params[0]);
        }
    }

    class CheckCommand implements Command {
        public boolean isPresent(String data) {
            return "check".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            click(params[0]);
        }
    }

    class SelectCommand implements Command {
        public boolean isPresent(String data) {
            return "select".equals(data);
        }
        public void execute(String ... params)
            throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            select(params[0].replaceFirst("(\\S+).*", "$1"), params[0].replaceFirst("\\S+\\s+", ""));
        }
    }

    /**
     * Test file from html content.
     *
     * @param baseUrl the base site url
     * @param xmlFileName xml file name without extention, placed in testdata directory.
     * @throws Exception in case of general exceptions
     */
    public void baseTestSelenium(String baseUrl, String xmlFileName) throws Exception {
        initTestData(xmlFileName);

        List<CommandData> commands = getSrcData();
        Command[] commandBlocks = new Command[] {new OpenCommand(), new AssertElementPresentCommand(),
                new AssertElementNotPresentCommand(), new AssertAttributeCommand(),
                new ClickCommand(), new ClickAndWaitCommand(), new TypeCommand(),
                new WaitForElementPresentCommand(), new AssertTitleCommand(),
                new EchoCommand(), new GoBackAndWaitCommand(), new SelectFrameCommand(),
                new WaitForAttribute(), new CheckCommand(), new SelectCommand()};
        for (CommandData command : commands) {
            boolean commandFound = false;
            for (Command commandBlock : commandBlocks) {
                if (commandBlock.isPresent(command.getCommand())) {
                    commandFound = true;
                    commandBlock.execute(command.getData1(), baseUrl);
                }
            }
            if (!commandFound) {
                fail("Unsupported command " + command.getCommand());
            }
        }
    }

    /**
     * Test file from XML content.
     *
     * @param fileName xml file name without extention, placed in testdata directory.
     * @throws Exception in case of general exceptions
     */
    public void testSelenium(String fileName) throws Exception {
        baseTestSelenium(BASE_URL, fileName);
    }

}
